/**
 * 
 */
package qy.jalgotrade.dataseries.resampled;

import java.time.ZonedDateTime;
import java.util.Map;

import qy.jalgotrade.bar.Bar;
import qy.jalgotrade.bar.Bar.Frequency;
import qy.jalgotrade.dataseries.BarDataSeries;
import qy.jalgotrade.dataseries.DataSeries;
import qy.jalgotrade.event.EventHandler;
import qy.jalgotrade.utils.resamplebase.Grouper;
import qy.jalgotrade.utils.resamplebase.TimeRange;

/**
 * <pre>
 * A BarDataSeries that will build on top of another, higher frequency, BarDataSeries. Resampling
 * will take place as new values get pushed into the dataseries being resampled.
 * 
 * .. note::
 *     * Supported resampling frequencies are:
 *         * Less than bar.Frequency.DAY
 *         * bar.Frequency.DAY
 *         * bar.Frequency.MONTH
 * 
 * 基于另一个 更高频的 BarDataSeries (x) 而构建的 BarDataSeries (y), 每当新的 value 被 push 进 x 时, 对 y 进行 resample 操作;
 * 支持的时间周期: 日内, 日, 月.
 * </pre>
 * 
 * @author qy
 *
 */
public class ResampledBarDataSeries extends BarDataSeries implements DSResampler<Bar> {

	private class BarDSResamplerImpl implements DSResampler<Bar>, EventHandler {

		private Frequency __frequency;

		private Grouper<Bar> __grouper;

		private TimeRange __range;

		/**
		 * 
		 */
		public BarDSResamplerImpl() {

		}

		/**
		 * 
		 * @param dataSeries
		 * @param frequency
		 */
		public void initDSResampler(BarDataSeries dataSeries, Frequency frequency) {

			__frequency = frequency;
			__grouper = null;
			__range = null;

			dataSeries.getNewValueEvent().subscribe(this);
		}

		/*
		 * 
		 */
		@Override
		public void handle(String eventName, Map<String, Object> params) throws Exception {

			DataSeries<?> dataSeries = (DataSeries<?>) params.get("dataSeries");
			ZonedDateTime dateTime = (ZonedDateTime) params.get("dateTime");
			Bar value = (Bar) params.get("value");
			__onNewValue(dataSeries, dateTime, value);
		}

		/**
		 * 
		 * @param dataSeries
		 * @param dateTime
		 * @param value
		 * @throws Exception
		 */
		private void __onNewValue(DataSeries<?> dataSeries, ZonedDateTime dateTime, Bar value) throws Exception {

			if (__range == null) {

				__range = TimeRange.buildRange(dateTime, __frequency);
				__grouper = buildGrouper(__range, value, __frequency);

			} else if (__range.belongs(dateTime)) {

				__grouper.addValue(value);

			} else {

				ResampledBarDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__range = TimeRange.buildRange(dateTime, __frequency);
				__grouper = buildGrouper(__range, value, __frequency);
			}
		}

		/*
		 * 
		 */
		@Override
		public Grouper<Bar> buildGrouper(TimeRange range, Bar value, Frequency frequency) {

			return new BarGrouper(range.getBeginning(), value, frequency);
		}

		/*
		 * 
		 */
		@Override
		public void pushLast() throws Exception {

			if (__grouper != null) {
				ResampledBarDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__grouper = null;
				__range = null;
			}
		}

		/**
		 * Forces a resample check. Depending on the resample frequency, and the current datetime, a new
		 * value may be generated.
		 */
		@Override
		public void checkNow(ZonedDateTime dateTime) throws Exception {

			if (__range != null && !!__range.belongs(dateTime)) {
				ResampledBarDataSeries.this.appendWithDateTime(__grouper.getDateTime(), __grouper.getGrouped());
				__grouper = null;
				__range = null;
			}
		}
	}

	private BarDSResamplerImpl __barDsResamplerImpl;

	/**
	 * 
	 * @param dataSeries
	 * @param frequency
	 * @throws Exception
	 */
	public ResampledBarDataSeries(BarDataSeries dataSeries, Frequency frequency) throws Exception {

		this(dataSeries, frequency, 0);
	}

	/**
	 * 
	 * @param dataSeries The DataSeries instance being resampled.
	 * @param frequency  The grouping frequency in seconds. Must be > 0.
	 * @param maxLen     The maximum number of values to hold. Once a bounded length is full, when new
	 *                   items are added, a corresponding number of items are discarded from the
	 *                   opposite end.
	 * @throws Exception
	 */
	public ResampledBarDataSeries(BarDataSeries dataSeries, Frequency frequency, int maxLen) throws Exception {

		super(maxLen);
		__barDsResamplerImpl = new BarDSResamplerImpl();
		__barDsResamplerImpl.initDSResampler(dataSeries, frequency);
	}

	/*
	 * 
	 */
	@Override
	public Grouper<Bar> buildGrouper(TimeRange range, Bar value, Frequency frequency) {

		return __barDsResamplerImpl.buildGrouper(range, value, frequency);
	}

	/*
	 * 
	 */
	@Override
	public void pushLast() throws Exception {

		__barDsResamplerImpl.pushLast();
	}

	/**
	 * Forces a resample check. Depending on the resample frequency, and the current datetime, a new
	 * value may be generated.
	 */
	@Override
	public void checkNow(ZonedDateTime dateTime) throws Exception {

		__barDsResamplerImpl.checkNow(dateTime);
	}
}
